﻿using System;
using System.Collections;

namespace CSharpExecutor
{
	public static class RequireVariableExtensions
	{
		public static Require.RequireVariable<T> IsInRange<T>(this Require.RequireVariable<T> @this, T inclusiveMinimumValue, T exclusiveMaximumValue)
			where T : IComparable
		{
			if (@this == null)
			{
				throw new ArgumentNullException("this");
			}

			if ((@this.Value.CompareTo(inclusiveMinimumValue) < 0) || (@this.Value.CompareTo(exclusiveMaximumValue) >= 0))
			{
				throw new ArgumentOutOfRangeException(@this.Name, string.Format("{0} = {1}; must be >= {2} and <= {3}", @this.Name, @this.Value, inclusiveMinimumValue, exclusiveMaximumValue));
			}

			return @this;
		}

		public static Require.RequireVariable<T> IsNotEmpty<T>(this Require.RequireVariable<T> @this)
			where T : ICollection
		{
			if (@this == null)
			{
				throw new ArgumentNullException("this");
			}

			if (@this.Value.Count == 0)
			{
				throw new ArgumentException("Cannot be empty.", @this.Name);
			}

			return @this;
		}

		public static Require.RequireVariable<Array> IsNotEmpty<T>(this Require.RequireVariable<Array> @this)
		{
			if (@this == null)
			{
				throw new ArgumentNullException("this");
			}

			if (@this.Value.Length == 0)
			{
				throw new ArgumentException("Cannot be empty.", @this.Name);
			}

			return @this;
		}

		/// <summary>
		/// Throws an exception if the specified argument is not valid.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="this">The @this.</param>
		/// <param name="predicate">The predicate.</param>
		/// <param name="invalidMessage">The invalid message.</param>
		/// <returns></returns>
		public static Require.RequireVariable<Type> IsAssignableTo(this Require.RequireVariable<Type> @this, Type type)
		{
			if (@this == null)
			{
				throw new ArgumentNullException("this");
			}

			if (!type.IsAssignableFrom(@this.Value))
			{
				throw new ArgumentException(string.Format("Must be assignable to '{0}.'", type), @this.Name);
			}

			return @this;
		}

		/// <summary>
		/// Throws an exception is the specified argument is null.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="this">The @this.</param>
		/// <returns></returns>
		public static Require.RequireVariable<T> IsNotNull<T>(this Require.RequireVariable<T> @this) where T : class
		{
			if (@this == null)
			{
				throw new ArgumentNullException("this");
			}

			if (@this.Value == null)
			{
				throw new ArgumentNullException(@this.Name);
			}

			return @this;
		}

		/// <summary>
		/// Throws an exception if the specified argument is null or empty.
		/// </summary>
		/// <param name="this">The @this.</param>
		/// <returns></returns>
		public static Require.RequireVariable<string> IsNotNullOrEmpty(this Require.RequireVariable<string> @this)
		{
			if (@this == null)
			{
				throw new ArgumentNullException("this");
			}

			if (string.IsNullOrEmpty(@this.Value))
			{
				throw new ArgumentException("Cannot be null or empty.", @this.Name);
			}

			return @this;
		}

		/// <summary>
		/// Throws an exception if the specified argument is not valid.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="this">The @this.</param>
		/// <param name="predicate">The predicate.</param>
		/// <param name="invalidMessage">The invalid message.</param>
		/// <returns></returns>
		public static Require.RequireVariable<T> IsValid<T>(this Require.RequireVariable<T> @this, Func<T, bool> predicate, string invalidMessage)
		{
			if (@this == null)
			{
				throw new ArgumentNullException("this");
			}

			if (!predicate(@this.Value))
			{
				throw new ArgumentException(invalidMessage, @this.Name);
			}

			return @this;
		}

		public static Require.RequireVariable<T[]> IsOfLength<T>(this Require.RequireVariable<T[]> @this, int length)
		{
			if (@this == null)
			{
				throw new ArgumentNullException("this");
			}

			if (@this.Value.Length != length)
			{
				throw new ArgumentException(string.Format("Array length = {0}; must equal {1}.", @this.Value.Length, length), @this.Name);
			}

			return @this;
		}
	}
}